from bs4 import BeautifulSoup
'''
Beautiful Soup 将复杂的 HTML 文档转换成一个复杂的树形结构，每个节点都是 Python 对象，所有对象可以归纳为 4 种: Tag，NavigableString，BeautifulSoup，Comment。

Tag 就是 HTML 的一个标签，比如 div，p 标签等，也是我们用的最多的一个对象。

NavigableString 指标签内部的文字，直译就是可遍历的字符串。

BeautifulSoup 指一个文档的全部内容，可以当成一个 Tag 来处理。

Comment 是一个特殊的 NavigableString，其输出内容不包括注视内容
Beautiful Soup 和 requests 库结合使用体验更佳
'''
# soup = BeautifulSoup("<html><head><title>index</title></head><body>content</body></html>", "lxml") # 指定解析器
# print(soup.head)

html_doc = """
<html><head><title>index</title></head>
<body>
<p class="title"><b>首页</b></p>
<p class="main">我常用的网站
<a href="https://www.google.com" class="website" id="google">Google</a>
<a href="https://www.baidu.com" class="website" id="baidu">Baidu</a>
<a href="https://cn.bing.com" class="website" id="bing">Bing</a>
</p>
<div><!--这是注释内容--></div>
<p class="content1">...</p>
<p class="content2">...</p>
</body>
"""
# demo 2
# soup = BeautifulSoup(html_doc, "lxml")
# p_tag = soup.p
# print(p_tag.name)
# print(p_tag["class"])
# print(p_tag.attrs)
#
# p_tag.name="myTag" # attrs 同样可被修改，操作同字典
# print(p_tag)

# demo 3 获取更深层次的标签
# soup = BeautifulSoup(html_doc, "lxml")
# print(soup.p.b)

# demo 4 如果想获得所有的某个名字的标签，则可以使用 find_all(tag_name) 函数。
# soup = BeautifulSoup(html_doc, "lxml")
# a_tags=soup.find_all("a")
# print(a_tags)

# demo 5 我们可以使用 .contents 将 tag 以列表方式输出，即将 tag 的子节点格式化为列表，这很有用，意味着可以通过下标进行访问指定节点。同时我们还可以通过 .children 生成器对节点的子节点进行遍历
# soup = BeautifulSoup(html_doc, "lxml")
# head_tag=soup.head
# print(head_tag)
# print(head_tag.contents)
#
# for child in head_tag.children:
# 	print("child is : ", child)
# demo 6 .children 只可以获取 tag 的直接节点，而获取不到子孙节点，.descendants 可以满足你
# soup = BeautifulSoup(html_doc, "lxml")
# head_tag=soup.head
# for child in head_tag.descendants:
# 	print("child is : ", child)
#  demo 7 通过 .parent 属性获取标签的父亲节点。 title 的父标签是 head，html 的父标签是 BeautifulSoup 对象，而 BeautifulSoup 对象的父标签是 None
# soup = BeautifulSoup(html_doc, "lxml")
# title_tag=soup.title
#
# print(title_tag.parent)
# print(type(soup.html.parent))
# print(soup.parent)

# # demo 8 同时，我们可以通过 parents 得到指定标签的所有父亲标签
# soup = BeautifulSoup(html_doc, "lxml")
# a_tag=soup.a
#
# for parent in a_tag.parents:
#     print(parent.name)

# # demo 9 通过 .next_sibling 和 .previous_sibling 来获取下一个标签和上一个标签。
# soup = BeautifulSoup(html_doc, "lxml")
# div_tag=soup.div
#
# print(div_tag.next_sibling)
# div 的第一个 next_sibling 是div 和 p 之间的换行符。这个规则对于 previous_sibling 同样适用
# print(div_tag.next_sibling.next_sibling)
# demo 10 我们可以通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出。在该例子中，我们在每次输出前加了前缀，这样就可以更直观的看到 dib 的第一个 previous_sibling 是换行符了
# soup = BeautifulSoup(html_doc, "lxml");
# div_tag=soup.div
# for pre_tag in div_tag.previous_siblings:
#     print("pre_tag is :",pre_tag)

# TODO 前进和后退
'''
通过 .next_element 和 .previous_element 获取指定标签的前一个或者后一个被解析的对象，
注意这个和兄弟节点是有所不同的，兄弟节点是指有相同父亲节点的子节点，而这个前一个或者后一个是按照文档的解析顺序来计算的
'''
# demo 11
# soup = BeautifulSoup(html_doc, "lxml");
#
# head_tag=soup.head
# print(head_tag.next_element)
#
# title_tag=soup.title
# # 需要注意的是 title 下一个解析的标签不是 body，而是 title 标签内的内容，因为 html 的解析顺序是打开 title 标签，然后解析内容，最后关闭 title 标签
# print(title_tag.next_element)

# 另外，我们同样可以通过 .next_elements 和 .previous_elements 来迭代文档树。由以下例子我们可以看出，换行符同样会占用解析顺序，与迭代兄弟节点效果一致
# soup = BeautifulSoup(html_doc, "lxml");
# div_tag=soup.div
# for next_element in div_tag.next_elements:
# 	print("next_element is : ", next_element)
# 详细api文档： https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#
